package com.nutstudy.ioc.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: HyoJoo-W
 * @date: 2021-07-29 09:33
 * @description: 容器
 */


public class NutApplicationContext
{
    private Class<?> configClass;

    //存放bean的定义信息
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    //存放bean(单例池)
    private Map<String, Object> singletonObjects = new HashMap<>();

    //后置处理器列表
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public NutApplicationContext(Class<?> configClass)
    {
        this.configClass = configClass;

        //扫描组件
        scan(configClass);

        //实例化单例对象
        preInstantiateSingletons();
    }

    /**
     * @param configClass: 传入的配置文件Class对象
     * @desc: 扫描bean装入容器
     */
    private void scan(Class<?> configClass)
    {
        //解析传入的配置类
        //获取扫描路径
        NutComponentScan annotation = configClass.getAnnotation(NutComponentScan.class);
        String path = annotation.value();
        path = path.replace(".", "/");
//        System.out.println("path = " + path);

        //扫描找到有ComponentScan注解的Class文件 -> 生成Bean
        ClassLoader classLoader = NutApplicationContext.class.getClassLoader();

        URL resource = classLoader.getResource(path);

        assert resource != null;
        File file = new File(resource.getFile());

        File[] files = file.listFiles();
        assert files != null;
        for (File item : files)
        {
            //验证
            String absolutePath = item.getAbsolutePath();
            if (absolutePath.endsWith(".class"))
            {
                String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));

                className = className.replace("\\", ".");
                try
                {
                    Class<?> clazz = classLoader.loadClass(className);

                    if (clazz.isAnnotationPresent(NutComponent.class))
                    {
                        //后置处理器
                        if (BeanPostProcessor.class.isAssignableFrom(clazz))
                        {
                            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                            beanPostProcessorList.add(beanPostProcessor);
                        }

                        //bean扫描
                        NutComponent annotation1 = clazz.getAnnotation(NutComponent.class);
                        String beanName = annotation1.value();

                        //装配bean的class属性
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setClazz(clazz);

                        //装配bean的scope属性
                        if (clazz.isAnnotationPresent(NutScope.class))
                        {
                            NutScope annotation2 = clazz.getAnnotation(NutScope.class);
                            beanDefinition.setScope(annotation2.value());
                        } else
                        {
                            beanDefinition.setScope("singleton");
                        }

                        //将bean相关信息添加到容器中
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }

                }
                catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @desc: 实例化单例对象
     */
    private void preInstantiateSingletons()
    {
        //(根据扫描到的bean定义)创建单例池---容器创建时
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet())
        {
            BeanDefinition beanDefinition = entry.getValue();
            String beanName = entry.getKey();
            if (beanDefinition.getScope().equals("singleton"))
            {
                //创建bean
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    /**
     * @param beanName
     * @param beanDefinition
     * @return
     * @desc: 创建Bean
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition)
    {
        try
        {
            Class<?> clazz = beanDefinition.getClazz();
            Object instance = clazz.newInstance();

            //依赖注入
            for (Field field : clazz.getDeclaredFields())
            {
                if (field.isAnnotationPresent(NutAutowired.class))
                {
                    String name = field.getName();
                    Object bean = getBean(name);

                    field.setAccessible(true);
                    field.set(instance, bean);
                }
            }

            //后置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList)
            {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            //初始化器
            if (instance instanceof InitializingBean)
            {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            //后置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList)
            {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            return instance;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * @param beanName: bean的名字
     * @return
     * @desc: 根据bean的名字获取bean的实例化对象
     */
    public Object getBean(String beanName)
    {
        if (beanDefinitionMap.containsKey(beanName))
        {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton"))
            {
                //单例
                return singletonObjects.get(beanName);
            } else
            {
                //原型模式
                return createBean(beanName, beanDefinition);
            }
        } else
        {
            throw new NullPointerException();
        }
    }

}
